home *** CD-ROM | disk | FTP | other *** search
- NAME PM_AT
- PAGE 60, 132
- .286C
- ;
- ; PM/AT - A program to place the PC/AT into Protected Mode
- ; Copyright 1985, Ross P. Nelson
- ;
-
- INCLUDE \usr\include\protect.inc
-
- ; Data structure definitions
- DESCRIP STRUC ; generic descriptor format
- limit DW ? ; offset if gate
- phys_addr_lo DW ? ; selector if gate
- phys_addr_hi DB ? ; wc if gate
- access DB ? ; access rights
- DW 0 ; reserved for 386
- DESCRIP ENDS
-
- TSS_BLOCK STRUC ; format of a TSS
- back_link DW ? ; previously active TSS
- rSP0 DW ? ; level 0 stack
- rSS0 DW ?
- rSP1 DW ? ; level 1 stack
- rSS1 DW ?
- rSP2 DW ? ; level 2 stack
- rSS2 DW ?
- rIP DW ?
- FLAGS DW ?
- rAX DW ?
- rCX DW ?
- rDX DW ?
- rBX DW ?
- rSP DW ?
- rBP DW ?
- rSI DW ?
- rDI DW ?
- rES DW ?
- rCS DW ?
- rSS DW ? ; active stack segment
- rDS DW ?
- task_LDT DW ? ; LDT selector
- TSS_BLOCK ENDS
-
- ; Literal values for descriptor types
- TSS EQU 1
- LDT EQU 2
- TSS_BUSY EQU 3
- CALL_GATE EQU 4
- TASK_GATE EQU 5
- INT_GATE EQU 6
- TRAP_GATE EQU 7
-
- RDONLY EQU 0 ; read only
- RD_WR EQU 1 ; read/write
- RD_WR_XD EQU 3 ; read/write expand down
- EXONLY EQU 4 ; execute only
- EX_RD EQU 5 ; execute/readable
- EXONLY_CF EQU 6 ; execute only/conforming
- EX_RD_CF EQU 7 ; execute/readable/conforming
-
- TSS_LIMIT EQU 43
-
- ; Segment building macros
- MSEG MACRO name,type,priv,combine ;; start a memory segment
- name SEGMENT PARA combine ;; MASM directive
- zero = $ ;; for ALIGN macro
- &name&_start = $ ;; origin
- &name&_ar = 90h OR (priv SHL 5) OR (type SHL 1) ;; access rights
- ENDM
-
- SSEG MACRO name,type,priv ;; start a system segment
- name SEGMENT PARA ;; MASM directive
- zero = $ ;; for ALIGN macro
- &name&_start = $ ;; origin
- &name&_ar = 80h OR (priv SHL 5) OR type ;; access rights
- ENDM
-
- ENDSEG MACRO name ;; terminate a segment
- &name&_limit = $ - &name&_start - 1 ;; create variable for seg limit
- name ENDS ;; limit <- size-1 (0-FFFFh)
- ENDM
-
- ; Descriptor building macros
- DSCRP MACRO export,name ;; build descrip for segment
- IFDIF <export>,<> ;; check for export name
- export LABEL WORD
- ENDIF
- DW &name&_limit ;; segment limit
- DW name ;; 16-bit segment addr
- DB 0 ;; high order addr
- DB &name&_ar ;; access rights
- DW 0 ;; reserved
- ENDM
-
- GATE MACRO export,offset,select,wc,type,priv ;; build descriptor
- IFDIF <export>,<> ;; check for export name
- export LABEL WORD
- ENDIF
- DW offset ;; offset
- DW select ;; segment selector
- DB wc ;; word count
- DB 80h OR (priv SHL 5) + type ;; access rights
- DW 0 ;; reserved
- ENDM
-
- ; Selector creating macros for Task segments
- GDT_SEL MACRO sel,priv
- DW sel + priv ;; assume sel = index * 8
- ENDM
-
- LDT_SEL MACRO sel,priv
- DW sel + 4 + priv ;; like GDT but TI bit set
- ENDM
-
- ; Utility macros
- CALL_EX MACRO sel,rpl ;; call exported item
- DB 9Ah ;; FAR call
- DW 0 ;; no offset
- DW sel + rpl ;; selector with req. priv.
- ENDM
-
- ALIGN MACRO bound ;; align $ on power of 2 bounds
- LOCAL diff
- diff = (($ - zero) AND (bound - 1)) ;; distance from bound
- IF diff NE 0 ;; if on bound skip
- ORG $ + (bound - diff) ;; else adjust
- ENDIF
- ENDM
-
- PAGE
- ; This segment contains the Global Descriptor Table
-
- MSEG GDT,RD_WR,0
- ; Required by INT 15
- DESCRIP <0,0,0,0> ; GDT(0) always blank
- DSCRP int15_gdt_dat,GDT ; DATA -> GDT
- DSCRP int15_idt_dat,IDT ; DATA -> IDT
- DSCRP ,DSC ; DATA -> DS
- DSCRP ,DSC ; DATA -> ES
- DSCRP ,DSC ; STACK -> SS
- DSCRP ,INIT ; CODE -> CS
- DESCRIP <0,0,0,0> ; CODE -> BIOS/int 15 reserved
- DSCRP setup_tss,INIT_TSS ; TSS -> initial task
- ; Mini BIOS
- DSCRP bio_dat,MBDAT ; DATA -> mini bios
- DSCRP bios_seg,BIOS ; CODE -> mini bios
- DSCRP disp_mono,MONO_RAM ; DATA -> monochrome display
- DSCRP disp_color,COLOR_RAM ; DATA -> color display
- ; Fault handlers
- DSCRP task_df,FTASK8 ; TSS -> double fault
- xtra8 DESCRIP <ftask8_limit,FTASK8,0,92h> ; writable DATA alias for TSS
- DSCRP task_tf,FTASK10 ; TSS -> task fault
- xtra10 DESCRIP <ftask10_limit,FTASK10,0,92h> ; writable DATA alias for TSS
- DSCRP task_sf,FTASK12 ; TSS -> task fault
- xtra12 DESCRIP <ftask12_limit,FTASK12,0,92h> ; writable DATA alias for TSS
- DSCRP fault_dat,FDAT ; DATA -> handler
- DSCRP fhandler,HAND ; CODE -> handler
- DSCRP falias,FDAT ; free for fault handler use
- ; Shared library
- DSCRP share_lib,SHLIB ; CODE -> shared
- GATE share_gate,shlib_start,share_lib,0,CALL_GATE,3 ; GATE to code
- ; Second task
- DSCRP task2_tss,TASK2 ; TSS for 2nd task
- DSCRP task2_ldt,T2LDT ; LDT for 2nd task
- ; Future use
- DESCRIP <0,0,0,0> ; available
- DESCRIP <0,0,0,0> ; available
- DESCRIP <0,0,0,0> ; available
- DESCRIP <0,0,0,0> ; available
- ENDSEG GDT
-
- PAGE
- ; This segment contains the Interrupt Descriptor Table.
-
- MSEG IDT,RD_WR,0
- ; Chip level interrupts (0 - 1Fh)
- GATE ,fault_00,fhandler,0,TRAP_GATE,0 ; DIVIDE
- GATE ,fault_01,fhandler,0,TRAP_GATE,0 ; TRAP
- GATE ,fault_02,fhandler,0,TRAP_GATE,0 ; NMI
- GATE ,fault_03,fhandler,0,TRAP_GATE,0 ; BRKPT
- GATE ,fault_04,fhandler,0,TRAP_GATE,0 ; INTO
- GATE ,fault_05,fhandler,0,TRAP_GATE,0 ; BOUND
- GATE ,fault_06,fhandler,0,TRAP_GATE,0 ; undef
- GATE ,fault_07,fhandler,0,TRAP_GATE,0 ; 287 NAVAIL
- GATE ,0,task_df,0,TASK_GATE,0 ; DBL FAULT
- GATE ,fault_09,fhandler,0,TRAP_GATE,0 ; 287 OVRRUN
- GATE ,0,task_tf,0,TASK_GATE,0 ; TSS FAULT
- GATE ,fault_11,fhandler,0,TRAP_GATE,0 ; NP FAULT
- GATE ,0,task_sf,0,TASK_GATE,0 ; STACK FAULT
- GATE ,fault_13,fhandler,0,TRAP_GATE,0 ; GP FAULT
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- GATE ,fault_16,fhandler,0,TRAP_GATE,0 ; 287 ERROR
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- GATE ,unknown,fhandler,0,TRAP_GATE,0
- ; System interrupts
- ; Hardware Level 0 (20-27) DOS equivalent vector
- GATE ,timer_int,bios_seg,0,INT_GATE,0 ; 8
- GATE ,kb_int,bios_seg,0,INT_GATE,0 ; 9
- GATE ,rsrv_int,bios_seg,0,INT_GATE,0 ; A
- GATE ,com1_int,bios_seg,0,INT_GATE,0 ; B
- GATE ,com2_int,bios_seg,0,INT_GATE,0 ; C
- GATE ,prn2_int,bios_seg,0,INT_GATE,0 ; D
- GATE ,fd_int,bios_seg,0,INT_GATE,0 ; E
- GATE ,prn1_int,bios_seg,0,INT_GATE,0 ; F
- ; Hardware Level 1 (28-2F)
- GATE ,rtc_int,bios_seg,0,INT_GATE,0 ; 70
- GATE ,rsrv_int,bios_seg,0,INT_GATE,0 ; 71
- GATE ,rsrv_int,bios_seg,0,INT_GATE,0 ; 72
- GATE ,rsrv_int,bios_seg,0,INT_GATE,0 ; 73
- GATE ,rsrv_int,bios_seg,0,INT_GATE,0 ; 74
- GATE ,n287_int,bios_seg,0,INT_GATE,0 ; 75
- GATE ,hd_int,bios_seg,0,INT_GATE,0 ; 76
- GATE ,rsrv_int,bios_seg,0,INT_GATE,0 ; 77
- ; Mini BIOS (30 - 31)
- GATE ,int_30,bios_seg,0,TRAP_GATE,3
- GATE ,sw_reset,bios_seg,0,TRAP_GATE,0
- ENDSEG IDT
-
- PAGE
- ; Mini BIOS
- ; This section contains the "miniBIOS," a collection of
- ; routines for hardware support, including the interrupt
- ; handlers, and user-callable display routines.
-
- ; PC/AT Hardware Control
- MSEG MONO_RAM,RD_WR,0,<AT 0B000h>
- ORG 4000 ; end of monochrome RAM
- ENDSEG MONO_RAM
-
- MSEG COLOR_RAM,RD_WR,0,<AT 0B800h>
- ORG 16 * 1024 ; end of color RAM
- ENDSEG COLOR_RAM
-
- MASTER EQU 20h ; master 8259A
- SLAVE EQU 0A0h ; slave 8259A
- DEV_COLOR EQU 3D4h ; color port
- RETRACE_PORT EQU 3DAh ; port for horiz/vert retrace
- DEV_MONO EQU 3B4h ; monochrome port
- DEV_RTC EQU 70h ; real-time-clock port
-
- EOI EQU 20h ; end of interrupt command
-
- WR_DEVICE MACRO device,unit,data ; write to rtc or crt devices
- IFDIF <device>,<>
- mov dx, device
- ENDIF
- mov al, unit
- out dx, al
- inc dx
- mov al, data
- out dx, al
- dec dx
- ENDM
-
-
- MSEG MBDAT,RD_WR,0
-
- tick_ctr DD 0 ; incremented by timer int
- kb_ctr DW -2 ; keyboard interrupts
- ; NOTE: This program is setup to run on a monochrome system only
- ; This pointer must be modified to support a color display.
- display_ptr LABEL DWORD ; points to display RAM
- DW 0 ; offset
- DW disp_mono ; selector (MONOCHROME)
- cursor LABEL word
- cursor_x DB 0 ; column
- cursor_y DB 0 ; row
- attrib DB 7
- ENDSEG MBDAT
-
- MSEG BIOS,EXONLY,0
- ASSUME CS:BIOS, DS:NOTHING
-
- ; INTERRUPT HANDLERS
- ; This is where the MINIBIOS comes when it gets a hardware interrupt.
- ; In this implementation, the only interrupt which is handled is
- ; the timer tick. The keyboard interrupt is also used as a signal
- ; to exit protected mode. The other handlers are left as an
- ; exercise for the user.
- ; Level 0 interrupts
- timer_int:
- push ax
- push ds
- mov ax, OFFSET bio_dat ; data seg selector
- mov ds, ax
- ASSUME DS:MBDAT
- inc WORD PTR tick_ctr ; bump counter
- adc WORD PTR tick_ctr[2], 0
- mov al, EOI ; signal 8259A
- out MASTER, al
- pop ds
- ASSUME DS:NOTHING
- pop ax
- iret
-
- kb_int:
- push ax
- mov al, EOI
- out MASTER, al
- pop ax
- int 31h ; RESET system
- iret
-
- com1_int:
- push ax
- mov al, EOI
- out MASTER, al
- pop ax
- iret
-
- com2_int:
- push ax
- mov al, EOI
- out MASTER, al
- pop ax
- iret
-
- prn2_int:
- push ax
- mov al, EOI
- out MASTER, al
- pop ax
- iret
-
- fd_int:
- push ax
- mov al, EOI
- out MASTER, al
- pop ax
- iret
-
- prn1_int:
- push ax
- mov al, EOI
- out MASTER, al
- pop ax
- iret
-
- ; Level 1 interrupts - must EOI both the SLAVE and MASTER 8259As
- rtc_int:
- push ax
- mov al, EOI
- out SLAVE, al
- out MASTER, al
- pop ax
- iret
-
- n287_int:
- push ax
- mov al, EOI
- out SLAVE, al
- out MASTER, al
- pop ax
- iret
-
- hd_int:
- push ax
- mov al, EOI
- out SLAVE, al
- out MASTER, al
- pop ax
- iret
-
- rsrv_int:
- int 1Fh ; cause failure
-
- PAGE
-
- ; MiniBIOS user callable function codes
- MBIOS_WR_CHAR EQU 0
- MBIOS_WR_STRING EQU 1
- MBIOS_WR_CRSR EQU 2
- MBIOS_WR_ATTR EQU 3
- MBIOS_BELL EQU 4
- MBIOS_CLS EQU 5
-
- ; USER CALLABLE FUNCTIONS
- ; INT 30h
- ; Write to display -- All registers but AX preserved
- ; FN: AH = 0 Write character
- ; Input: AL = char
- ;
- ; FN: AH = 1 Write ASCIIZ string
- ; Input DS:SI -> string
- ;
- ; FN: AH = 2 Set cursor
- ; Input: DH = row
- ; DL = column
- ;
- ; FN: AH = 3 Set attribute
- ; Input: AL = attribute
- ;
- ; FN: AH = 4 Bell
- ;
- ; FN AH = 5 Clear Screen
- ;
- int_30:
- cld
- or ah, ah ; determine function
- jz co
- dec ah
- jnz $ + 5
- jmp linout
- dec ah
- jnz $ + 5
- jmp set_cursor
- dec ah
- jnz $ + 5
- jmp set_attrib
- dec ah
- jnz $ + 5
- jmp bell
- dec ah
- jnz $ + 5
- jmp cls
- iret
-
- wr_cursor PROC NEAR
- ; Write HW cursor - cursor in DX, trashes AX, CX, DX
- mov ax, 80 ; convert to 16 bit
- mul dh
- xor dh, dh
- add dx, ax
- mov cx, dx
- WR_DEVICE DEV_MONO,14,ch ; write hardware
- WR_DEVICE ,15,cl
- ret
- wr_cursor ENDP
-
- co:
- push cx ; save state
- push di
- push ds
- push es
- mov cx, OFFSET bio_dat ; bios data segment
- mov ds, cx
- ASSUME DS:MBDAT
- les di, display_ptr
- mov ch, al ; save character
- mov ax, 80 * 2 ; number of columns/row
- mov cl, cursor_y ; time #rows
- mul cl
- add di, ax ; update offset
- xor ax, ax ; zero
- mov al, cursor_x ; column
- shl al, 1 ; * 2
- add di, ax ; update offset
- mov al, ch ; restore character
- mov ah, attrib ; get data
- stosw
- inc cursor_x ; ajust cursor position
- cmp cursor_x, 80
- jb co_done
- sub cursor_x, 80
- inc cursor_y
- co_done: push dx
- mov dx, cursor
- call wr_cursor
- pop dx
- pop es
- pop ds
- ASSUME DS:NOTHING
- pop di
- pop cx
- iret
-
- linout:
- push es ; save state
- push si
- push di
- push cx
- push ds
- mov cx, OFFSET bio_dat ; bios data segment
- mov ds, cx
- ASSUME DS:MBDAT
- les di, display_ptr ; get screen pointer
- mov ax, 80 * 2 ; number of columns/row
- mov cl, cursor_y ; time #rows
- mul cl
- add di, ax ; update offset
- xor ax, ax ; zero
- mov al, cursor_x ; column
- shl al, 1 ; * 2
- add di, ax ; update offset
- mov ah, attrib ; screen attribute
- pop ds ; user data
- ASSUME DS:NOTHING
- xor cx, cx ; count
- cld
- linout_loop: lodsb
- or al, al ; end of string?
- jz line_done ; yes - quit loop
- stosw ; no - write char/attrib
- inc cx
- jmp linout_loop ; write next char
- line_done: push ds
- mov ax, OFFSET bio_dat ; bios data segment
- mov ds, ax
- ASSUME DS:MBDAT
- mov ax, cx ; count
- mov cl, 80
- div cl ; al = rows, ah = columns
- add cursor_x, ah
- cmp cursor_x, 80 ; overflow?
- jb update_row ; no
- sub cursor_x, 80 ; else adjust
- inc al
- update_row: add cursor_y, al
- push dx
- mov dx, cursor
- call wr_cursor
- pop dx
- pop ds
- ASSUME DS:NOTHING
- pop cx
- pop di ; start of chars written
- pop si ; restore state
- pop es
- iret ; and return
-
- set_cursor:
- push cx
- push dx
- push ds
- mov ax, OFFSET bio_dat ; bios data segment
- mov ds, ax
- ASSUME DS:MBDAT
- mov cursor, dx ; save new cursor
- call wr_cursor
- pop ds
- ASSUME DS:NOTHING
- pop dx
- pop cx
- iret
-
- set_attrib:
- push cx
- push ds
- mov cx, OFFSET bio_dat ; bios data segment
- mov ds, cx
- ASSUME DS:MBDAT
- mov attrib, al
- pop ds
- ASSUME DS:NOTHING
- pop cx
- iret
-
- bell:
- push ax
- push bx
- push cx
- mov bx, 200
- in al, 61h ; get current state
- push ax ; save it
- bell_loop: and al, 0FCh ; speaker off
- out 61h, al
- mov cx, 60
- idle1: loop idle1
- or al, 002h ; speaker on
- out 61h, al
- mov cx, 180 ; duty cycle 1:3
- idle2: loop idle2
- dec bx ; test major loop
- jnz bell_loop
- pop ax
- out 61h, al ; restore state
- pop cx
- pop bx
- pop ax
- iret
-
- cls:
- push cx ; save state
- push dx
- push di
- push ds
- push es
- mov cx, OFFSET bio_dat ; bios data segment
- mov ds, cx
- ASSUME DS:MBDAT
- les di, display_ptr
- mov ah, attrib
- mov al, ' '
- mov cx, 80 * 25
- cld
- rep stosw
- xor dx, cx
- mov cursor, dx
- call wr_cursor
- pop es
- pop ds
- pop di
- pop dx
- pop cx
- iret
-
-
- ;
- ; INT 31
- ; Reset processor
- ;
- sw_reset PROC FAR
- WR_DEVICE DEV_RTC,0Fh,0 ; write SHUTDOWN code to RTC
- mov al, 0FEh ; HW SHUTDOWN
- out 64h, al ; HW STATUS
- halt: hlt
- jmp halt
- sw_reset ENDP
- ENDSEG BIOS
-
- PAGE
- ; FAULT HANDLERS
- ; In this prototype system, all the fault handler does is to display
- ; the name and location of the fault on the screen for a short period
- ; of time before resetting the system. This should provide the user
- ; with enough information to correct the problem.
-
- ; TSS for #DF - double fault handler
- ; #DF must have its own task to prevent shutdown
- SSEG FTASK8,TSS,0
- DW 0 ; back link
- DW 0, 0 ; SS0:SP - unneeded/CPL=0
- DW 0, 0 ; SS1:SP
- DW 0, 0 ; SS2:SP
- DW fault_ts ; IP
- DW 0 ; flags
- DW 4 DUP (0) ; AX/CX/DX/BX
- DW fhandler_stack ; SP
- DW fhandler_stack ; BP
- DW msg_08, 0 ; SI/DI
- GDT_SEL xtra8,0 ; ES
- GDT_SEL fhandler,0 ; CS
- GDT_SEL fault_dat,0 ; SS
- GDT_SEL fault_dat,0 ; DS
- DW 0 ; LDT selector
- ENDSEG FTASK8
-
- ; TSS for #TF - task fault handler
- ; #TF must have its own task to ensure a valid machine state
- SSEG FTASK10,TSS,0
- DW 0 ; back link
- DW 0, 0 ; SS0:SP - unneeded/CPL=0
- DW 0, 0 ; SS1:SP
- DW 0, 0 ; SS2:SP
- DW fault_ts ; IP
- DW 0 ; flags
- DW 4 DUP (0) ; AX/CX/DX/BX
- DW fhandler_stack ; SP
- DW fhandler_stack ; BP
- DW msg_10, 0 ; SI/DI
- GDT_SEL xtra10,0 ; ES
- GDT_SEL fhandler,0 ; CS
- GDT_SEL fault_dat,0 ; SS
- GDT_SEL fault_dat,0 ; DS
- DW 0 ; LDT selector
- ENDSEG FTASK10
-
- ; TSS for #SF - stack fault handler
- ; #SF requires its own task to prevent #DF in certain occasions
- SSEG FTASK12,TSS,0
- DW 0 ; back link
- DW 0, 0 ; SS0:SP - unneeded/CPL=0
- DW 0, 0 ; SS1:SP
- DW 0, 0 ; SS2:SP
- DW fault_ts ; IP
- DW 0 ; flags
- DW 4 DUP (0) ; AX/CX/DX/BX
- DW fhandler_stack ; SP
- DW fhandler_stack ; BP
- DW msg_12, 0 ; SI/DI
- GDT_SEL xtra12,0 ; ES
- GDT_SEL fhandler,0 ; CS
- GDT_SEL fault_dat,0 ; SS
- GDT_SEL fault_dat,0 ; DS
- DW 0 ; LDT selector
- ENDSEG FTASK12
-
- MSEG FDAT,RD_WR,0
- ; Data for fault handlers
- msg_00 DB "*** DIVIDE FAULT ***", 0
- msg_01 DB "*** SINGLE STEP TRAP ***", 0
- msg_02 DB "*** NMI ***", 0
- msg_03 DB "*** INT 3 ***", 0
- msg_04 DB "*** OVERFLOW EXCEPTION ***", 0
- msg_05 DB "*** BOUND EXCEPTION ***", 0
- msg_06 DB "*** UNDEFINED OPCODE ***", 0
- msg_07 DB "*** 287 NOT AVAILABLE ***", 0
- msg_08 DB "*** DOUBLE FAULT ***", 0
- msg_09 DB "*** 287 SEGMENT OVERRUN ***", 0
- msg_10 DB "*** ILLEGAL TSS FAULT ***", 0
- msg_11 DB "*** NOT PRESENT FAULT ***", 0
- msg_12 DB "*** STACK FAULT ***", 0
- msg_13 DB "*** GENERAL PROTECTION FAULT ***", 0
- msg_16 DB "*** 287 EXCEPTION ***", 0
- msg_fcode DB "*** Fault code = ",0
- msg_faddr DB "*** Fault address = ",0
- msg_unknown DB "*** UNKNOWN EXCEPTION ***", 0
- msg_buffer DB 40 DUP (0)
-
- ALIGN 2 ; force stack to word boundary
- DW 64 DUP (0)
- fhandler_stack LABEL WORD
-
- ENDSEG FDAT
-
- MSEG HAND,EXONLY,0
- ; Code for fault handlers
- ASSUME CS:HAND, DS:FDAT
-
- fault_00: mov si, OFFSET msg_00
- jmp fail
-
- fault_01: mov si, OFFSET msg_01
- jmp fail
-
- fault_02: mov si, OFFSET msg_02
- jmp fail
-
- fault_03: mov si, OFFSET msg_03
- jmp fail
-
- fault_04: mov si, OFFSET msg_04
- jmp fail
-
- fault_05: mov si, OFFSET msg_05
- jmp fail
-
- fault_06: mov si, OFFSET msg_06
- jmp fail
-
- fault_07: mov si, OFFSET msg_07
- jmp fail
-
- fault_08: mov si, OFFSET msg_08
- jmp fail
-
- fault_09: mov si, OFFSET msg_09
- jmp fail
-
- fault_10: mov si, OFFSET msg_10
- jmp fail
-
- fault_11: mov si, OFFSET msg_11
- jmp fail
-
- fault_12: mov si, OFFSET msg_12
- jmp fail
-
- fault_13: mov si, OFFSET msg_13
- jmp fail
-
- fault_16: mov si, OFFSET msg_16
- jmp fail
-
- unknown: mov si, OFFSET msg_unknown
- jmp fail
-
- ; All fault handlers that have a task switch come here
- fault_ts:
- pop ax ; error code
- mov bx, ES:[back_link] ; selector of faulting task
- lar dx, bx ; check if accessable
- jnz fake_data ; invalid
- test dh, 80h ; check present bit
- jz fake_data ; invalid
- and dh, 1Fh ; mask - leaving type info
- cmp dh, TSS_BUSY ; should point to user TSS
- jne fake_data ; invalid
- lsl dx, bx ; get segment size
- cmp dx, TSS_LIMIT ; ensure size OK
- jb fake_data ; branch too small
- ; At this point, we know that the back link points to a
- ; valid TSS, we now wish to create a readable data segment
- ; that points to the same physical location as the TSS so
- ; we can extract some information from it. Since this segment
- ; is created pointing to the same address as a previously
- ; existing segment, it is called an ALIAS
- mov di, OFFSET falias ; offset of free descriptor
- mov dx, OFFSET int15_gdt_dat; selector for GDT as
- mov es, dx ; if it were a data seg
- and bx, 0FFF8h ; convert selector to offset
- mov cx, ES:[bx].phys_addr_lo; get phys addr of user TSS
- mov ES:[di].phys_addr_lo, cx; store in free descriptor
- mov cl, ES:[bx].phys_addr_hi; continue with high byte
- mov ES:[di].phys_addr_hi, cl
- mov ES:[di].limit, TSS_LIMIT; complete free descriptor
- mov es, di ; use as selector to segment
- push ES:[rCS] ; push task's CS
- push ES:[rIP] ; push task's IP
- push ax ; error code
- jmp fail
-
-
- fake_data: push WORD PTR 0FFFFh ; can't get real info
- push WORD PTR 0FFFEh ; push false CS, IP
- push ax ; error code
- jmp fail
-
-
- pause PROC NEAR
- mov bx, 10
- ploop: mov cx, 0FFFFh
- loop $
- dec bx
- jnz ploop
- ret
- pause ENDP
-
- fail: mov ax, OFFSET fault_dat ; get legal DS
- mov ds, ax
- mov es, ax
- mov dx, 0 ; cursor x=0/y=0
- mov ah, MBIOS_WR_CRSR ; home cursor
- int 30h
- mov ah, MBIOS_WR_STRING ; write msg
- int 30h
- mov dx, 0100h ; cursor x=0/y=1
- mov ah, MBIOS_WR_CRSR ; home cursor
- int 30h
- ; check if error code on stack
- cmp si, OFFSET msg_08 ; was DF fault?
- je show_code
- cmp si, OFFSET msg_10 ; was TF fault?
- je show_code
- cmp si, OFFSET msg_11 ; was NP fault?
- je show_code
- cmp si, OFFSET msg_12 ; was SF fault?
- je show_code
- cmp si, OFFSET msg_13 ; was GP fault?
- jne show_addr
- show_code: mov si, OFFSET msg_fcode ; print code message
- mov ah, MBIOS_WR_STRING
- int 30h
- pop dx ; get code from stack
- mov di, OFFSET msg_buffer
- mov ah, LIB_BIN_HEX ; convert to hex
- CALL_EX share_gate,0
- mov si, OFFSET msg_buffer ; and print
- mov ah, MBIOS_WR_STRING
- int 30h
- mov dx, 0200h ; cursor x=0/y=2
- mov ah, MBIOS_WR_CRSR ; home cursor
- int 30h
- show_addr:
- mov si, OFFSET msg_faddr ; print addr message
- mov ah, MBIOS_WR_STRING
- int 30h
- pop bx ; get offset
- pop dx ; get segment
- push bx ; save offset
- mov di, OFFSET msg_buffer
- mov ah, LIB_BIN_HEX
- CALL_EX share_gate,0
- mov si, OFFSET msg_buffer
- mov ah, MBIOS_WR_STRING
- int 30h
- mov al, ':'
- mov ah, MBIOS_WR_CHAR
- int 30h
- pop dx ; offset
- mov di, OFFSET msg_buffer
- mov ah, LIB_BIN_HEX
- CALL_EX share_gate,0
- mov si, OFFSET msg_buffer
- mov ah, MBIOS_WR_STRING
- int 30h
- call pause ; wait
- call pause
- call pause
- mov ah, MBIOS_BELL ; bell
- int 30h
- call pause
- mov ah, MBIOS_BELL ; bell
- int 30h
- call pause
- mov ah, MBIOS_BELL ; bell
- int 30h
- call pause ; wait
- call pause
- call pause
- call pause
- int 31h ; reset processor
- ENDSEG HAND
-
- PAGE
- MSEG SHLIB,EX_RD_CF,0
- ;
- ; This segment implements a library of shared functions that may be
- ; invoked through gate "share_gate". The segment is conforming, so its
- ; code will run at the same privelege as the caller. The calling
- ; sequence is merely to set up the registers and CALL the gate. If an
- ; illegal function number is called, the system issues a DIVIDE BY 0.
- ; Only registers BP, SP, CS, DS, ES, and SS are guaranteed preserved.
- ;
- ASSUME CS:SHLIB, DS:NOTHING, ES:NOTHING
-
- LIB_SINT_BIN EQU 0
- LIB_UINT_BIN EQU 1
- LIB_HEX_BIN EQU 2
- LIB_BIN_SINT EQU 3
- LIB_BIN_UINT EQU 4
- LIB_BIN_HEX EQU 5
-
- shlib_code PROC FAR
- cld ; set direction for string fns
- cmp ah, 5 ; beyond last function?
- jbe index ; no - do indexing
- xor ax, ax ; zero ax
- div al ; force divide fault
-
- index: mov bl, ah ; get FN code
- xor bh, bh ; clear high order
- shl bx, 1 ; convert FN to index
- add bx, OFFSET table
- jmp WORD PTR CS:[bx] ; invoke function.
-
- table DW sint_bin
- DW uint_bin
- DW hex_bin
- DW bin_sint
- DW bin_uint
- DW bin_hex
-
- ; Function 0 / ASCII SIGNED INT to BINARY conversion
- ; AH = 0
- ; DS:SI -> Null terminated string of digits
- ; Returns:
- ; AX <- 16-bit signed integer
- ; CY <- set if error
- ;
- sint_bin:
- mov cx, 10 ; multiply constant
- xor ax, ax ; initialize accumulator
- xor dx, dx
- xor bh, bh ; sign flag FALSE
- cmp BYTE PTR [si], '-' ; signed?
- jne get_schar ; no
- inc si ; next char
- inc bh ; set signed flag
- get_schar: mov bl, [si] ; get input character
- inc si ; bump ptr
- or bl, bl ; end of string?
- jz set_sign
- cmp bl, '0' ; check valid
- jb err_ret
- cmp bl, '9'
- ja err_ret
- sub bl, '0' ; convert digit to binary
- mul cx ; decimal shift left
- add al, bl ; new digit
- adc ah, 0 ; propogate carry
- js err_ret ; quit if sign overflow
- adc dx, 0
- jnz err_ret ; quit if overflow
- jmp get_schar
- set_sign: or bh, bh ; sign flag on
- jz done ; no - return
- neg ax ; else complement
- done: clc ; no error
- ret
- err_ret: stc ; CY is error flag
- ret
-
- ; Function 1 / ASCII UNSIGNED INT to BINARY conversion
- ; AH = 1
- ; DS:SI -> Null terminated string of digits
- ; Returns:
- ; AX <- 16-bit unsigned integer
- ; CY <- set if error
- ;
- uint_bin:
- mov cx, 10 ; multiply constant
- xor ax, ax ; initialize accumulator
- xor dx, dx
- get_uchar: mov bl, [si] ; get input character
- inc si ; bump ptr
- or bl, bl ; end of string?
- jz done ; yes - return
- cmp bl, '0' ; check valid
- jb err_ret
- cmp bl, '9'
- ja err_ret
- sub bl, '0' ; convert digit to binary
- mul cx ; decimal shift left
- add al, bl ; new digit
- adc ah, 0 ; propogate carry
- adc dx, 0
- jnz err_ret ; quit if overflow
- jmp get_uchar
-
- ; Function 2 / ASCII HEX to BINARY conversion
- ; AH = 2
- ; DS:SI -> Null terminated string of digits
- ; Returns:
- ; AX <- 16-bit unsigned
- ; CY <- set if error
- ;
- hex_bin:
- xor dx, dx ; init accumulator
- get_hchar: lodsb ; get character
- or al, al ; last char?
- jnz test_hchars
- mov ax, dx
- ret ; CY cleared by OR
- test_hchars: cmp al, '0' ; check valid digit
- jb err_ret
- cmp al, '9'
- jbe got_valid
- or al, 20h ; must be alpha - force lower
- cmp al, 'a' ; check valid char
- jb err_ret
- cmp al, 'f'
- ja err_ret
- sub al, 27h ; adjust range
- got_valid: sub al, '0' ; convert digit to binary
- cmp dx, 0FFFh ; test overflow
- ja err_ret
- shl dx, 4 ; hex shift left
- add dl, al ; insert new digit
- jmp get_hchar
-
- ; Function 3 / BINARY to ASCII SIGNED INT conversion
- ; AH = 3
- ; DX -> 16-bit signed
- ; ES:DI -> Buffer for ascii string
- ; Returns:
- ; Null terminated ASCII string at ES:DI
- ;
- bin_sint: test dh, 80h ; sign bit?
- jz bin_uint ; no - treat as unsigned
- mov al, '-' ; else write sign
- stosb
- neg dx ; and complement
- jmp bin_uint
-
- div_tab DW 10000
- DW 1000
- DW 100
- DW 10
- DW 1
-
- ; Function 4 / BINARY to ASCII UNSIGNED INT conversion
- ; AH = 4
- ; DX -> 16-bit unsigned
- ; ES:DI -> Buffer for ascii string
- ; Returns:
- ; Null terminated ASCII string at ES:DI
- ;
- bin_uint: mov si, OFFSET div_tab ; index
- xor bx, bx ; bh is zero suppress flag
- mov ax, dx ; value
- u_loop: cmp WORD PTR CS:[si], 1 ; last divisor?
- je u_out ; yes - output last digit
- xor dx, dx ; high order zero
- div WORD PTR CS:[si] ; DX:AX/10^n
- or ax, bx ; quotient == 0 || ! suppress?
- jz u_loop
- mov bh, 1 ; turn off zero suppress flag
- add al, '0' ; quotient always single digit
- stosb
- mov ax, dx ; restore AX with remainder
- inc si
- inc si ; next divisor
- jmp u_loop
- u_out: add al, '0' ; last digit
- stosb
- xor al, al ; ASCII null
- stosb
- ret
-
- ; Function 5 / BINARY to ASCII HEX conversion
- ; AH = 5
- ; DX -> 16-bit unsigned
- ; ES:DI -> Buffer for ascii
- ; Returns:
- ; Null terminated 4 character ASCII string at ES:DI
- ;
- bin_hex: mov al, dh ; high order byte
- shr al, 4 ; high nybble
- add al, '0' ; convert to ASCII
- cmp al, '9' ; test value > '9'
- jbe bin_h1
- add al, 7 ; ajust alpha
- bin_h1: stosb
- mov al, dh ; high order byte
- and al, 0Fh ; low nybble
- add al, '0' ; convert to ASCII
- cmp al, '9' ; test value > '9'
- jbe bin_h2
- add al, 7 ; ajust alpha
- bin_h2: stosb
- mov al, dl ; low order byte
- shr al, 4 ; high nybble
- add al, '0' ; convert to ASCII
- cmp al, '9' ; test value > '9'
- jbe bin_h3
- add al, 7 ; ajust alpha
- bin_h3: stosb
- mov al, dl ; low order byte
- and al, 0Fh ; low nybble
- add al, '0' ; convert to ASCII
- cmp al, '9' ; test value > '9'
- jbe bin_h4
- add al, 7 ; ajust alpha
- bin_h4: stosb
- xor al, al
- stosb ; ASCII null
- ret
-
- shlib_code ENDP
- ENDSEG SHLIB
-
- PAGE
- ;
- ; This section contains the main code and data. We come here initially
- ; in Real Address Mode, perform necessary setup, and enter Protected
- ; Virtual Address Mode. The data segment has combine type STACK
- ; so that the linker will initialize SS:SP.
- ;
- MSEG DSC,RD_WR,0,STACK
-
- no_pm_msg DB '*** Unable to enter protected mode ***$'
- blank_line DB 80 DUP (' ')
- DB 0
- msg DB 'Testing',0
-
- ALIGN 2 ; force stack to word bound
- DW 100 DUP (?) ; stack
- ENDSEG DSC
-
- SSEG INIT_TSS,TSS,0
- TSS_BLOCK <> ; uninitialized
- ENDSEG INIT_TSS
-
- MSEG INIT,EXONLY,0
- ASSUME CS:INIT, DS:DSC
-
- adjust_addr PROC NEAR
- ; This subroutine marches through a descriptor table to fixup 16-bit
- ; segment addresses to full 24-bit physical addresses. Since the
- ; segment fixups were done by the DOS linker in Real Address Mode,
- ; all we need to do is multiply by 16. We assume the high order 8
- ; bits are zero, i.e., all addresses are in the first 1Mb.
- ; Called with ES:0 pointing to table, CX is number of entries.
- xor bx, bx ; initial offset
- l1: mov al, ES:[bx].access ; get access rights byte
- test al, 10h ; is descriptor a segment?
- jnz got_seg ; yes
- and al, 0Fh ; extract type
- cmp al, 3 ; gate?
- ja update_next ; yes - skip segment adjust
- got_seg: mov ax, ES:[bx].phys_addr_lo; get segment
- mov dx, 16
- mul dx ; convert to phys addr
- mov ES:[bx].phys_addr_lo, ax; store
- mov ES:[bx].phys_addr_hi, dl; 24 bits
- update_next: add bx, 8 ; incr to next descrip
- loop l1
- ret
- adjust_addr ENDP
-
- start:
- mov ax, DSC ; set up DS
- mov ds, ax
- sti
-
- ; When DOS created the prototype descriptors, it placed segment addresses
- ; in the physical address portion of the segment descriptors. We must
- ; fix up all descirptor tables which contain segment descriptors.
- mov ax, T2LDT
- mov es, ax ; point to proto LDT
- mov cx, t2ldt_limit ; get limit
- inc cx ; bump to size in bytes
- shr cx, 3 ; convert to # entries
- call adjust_addr
-
- mov ax, GDT
- mov es, ax ; point to proto GDT
- mov cx, gdt_limit
- inc cx
- shr cx, 3 ; gdt entries
- call adjust_addr
-
- ; Now we ask the BIOS to place us in protected mode. The BIOS requires
- ; the first 7 descriptors of the GDT to be setup as we have done. This
- ; gives it enough information to load GDTR and IDTR and setup a new
- ; code and data segment for the calling routine. The BIOS will also
- ; program the 8259A to our requested interrupt vectors. Additionally, it
- ; sets up the internal AT hardware to allow addresses > 1Mb to go out
- ; over the bus (frees A20 line).
- xor si, si ; ES:SI -> proto GDT
- mov bh, 20h ; int level 1 start
- mov bl, 28h ; int level 2 start
- mov ah, 89h ; enter PM request
- mov cx, 0FFFFh ; idle here to ensure all
- loop $ ; DOS keybd ints processed
- int 15h ; BIOS call
- jnc vm ; successful if no CY bit
-
- mov ah, 9 ; no - print message
- mov dx, OFFSET no_pm_msg
- int 21h
- mov ax, 4C01h ; failure
- int 21h ; exit
-
- ;;; NOW IN PROCTED MODE -- INTS DISABLED
-
- vm:
- mov bp, sp ; setup registers
- mov ax, ds
- mov es, ax
- mov ax, OFFSET setup_tss ; active task
- ltr ax
-
- pm_init_done:
- mov ah, MBIOS_BELL ; bell
- int 30h
- call idle
-
- mov ah, MBIOS_CLS ; cls
- int 30h
-
- ; Enable ints
- xor al, al ; no ints masked
- out MASTER+1, al
- out SLAVE+1, al
- sti
-
- ; Print number of ticks so far
- print_ticks:
- mov ah, MBIOS_CLS ; clear screen
- int 30h
- mov dx, 0010h
- mov ah, MBIOS_WR_CRSR
- int 30h
- mov ax, OFFSET bio_dat
- mov es, ax
- cli
- mov dx, WORD PTR ES:tick_ctr ; get tick counter
- mov ax, WORD PTR ES:tick_ctr[2]
- sti
- push dx
- call pr_hex_word ; print high order
- pop ax
- call pr_hex_word ; print low order
- call idle ; pause
-
- CALL_EX task2_tss,0 ; invoke task 2
-
- call idle
- call idle
- mov ah, MBIOS_BELL ; bell
- int 30h
- jmp print_ticks ; loop forever
-
-
- idle PROC NEAR
- push bx
- push cx
- mov bx, 10
- iloop: mov cx, 0FFFFh
- loop $
- dec bx
- jnz iloop
- pop cx
- pop bx
- ret
- idle ENDP
-
- pr_hex_word PROC
- ; Print word in AX
- push bp
- mov bp, sp
- sub sp, 10 ; space for string on stack
- push ds
- pop es ; es = ds
- lea di, [bp-10] ; destination
- mov dx, ax ; value
- mov ah, LIB_BIN_HEX ; function
- CALL_EX share_gate,0 ; shared code
- lea si, [bp-10] ; hex string ptr
- mov ah, MBIOS_WR_STRING ; function
- int 30h ; print string
- mov sp, bp
- pop bp
- ret
- pr_hex_word ENDP
- ENDSEG INIT
-
- PAGE
- ; Finally, we have a small second task, which will alternate execution
- ; with the initial task. It runs at privelege level 3, which means it
- ; has access only to its code segment, data segment, the shared library
- ; gate and INT 30h.
-
- SSEG T2LDT,LDT,0
- ; All memory segments for this task reside in a local descriptor table.
- DSCRP task2_cs,CODE2 ; CS for 2nd task
- DSCRP task2_dsc,DSC2 ; DS/SS for 2nd task
- DSCRP task2_stk0,STK2_0 ; Level 0 stack for OS calls
- ENDSEG T2LDT
-
- SSEG TASK2,TSS,0
- DW 0 ; back link
- DW STK2_0_limit + 1 ; SP0
- LDT_SEL task2_stk0,0 ; SS0
- DW 0, 0 ; SS1:SP
- DW 0, 0 ; SS2:SP
- DW top ; initial IP
- DW 0 ; flags
- DW 4 DUP (0) ; AX/CX/DX/BX
- DW stack2 ; SP
- DW stack2 ; BP
- DW 2 DUP (0) ; SI/DI
- LDT_SEL task2_dsc,3 ; ES - segments all in LDT
- LDT_SEL task2_cs,3 ; CS
- LDT_SEL task2_dsc,3 ; SS
- LDT_SEL task2_dsc,3 ; DS
- GDT_SEL task2_ldt,0 ; LDT selector
- ENDSEG TASK2
-
- MSEG STK2_0,RD_WR,0
- DW 128 DUP (?) ; stack for level 0 execution
- ENDSEG STK2_0
-
- MSEG DSC2,RD_WR,3
- ; Data and stack segment for 2nd task
- t2_msg DB "Task 2 running",0
-
- ALIGN 2 ; stack on word boundary
- DW 128 DUP (?)
- stack2 LABEL WORD
-
- ENDSEG DSC2
-
- MSEG CODE2,EXONLY,3
- ASSUME CS:CODE2, DS:DSC2
- top: ; task starts here first time
- mov dx, 0032h
- mov ah, MBIOS_WR_CRSR
- int 30h ; cursor to "safe" location
- mov si, OFFSET t2_msg
- mov ah, MBIOS_WR_STRING
- int 30h ; print message
- iret ; return to previous task
- jmp top ; when task invoked again,
- ; CS:IP points here (after IRET)
- ENDSEG CODE2
-
- END start
-